#pragma once
#include "child_pipe.hpp"
#include "function.hpp"
#include "smartptr.hpp"
class process_pool
{
public:
    process_pool(int size)
        : _size(size)
    {}

    //创建管道和子进程
    void Create()
    {
        //记录下父进程打开的写端
        vector<int> last_wfds;

        for(int i = 0; i < _size; ++i)
        {
            //创建管道
            int pfd[2]{0};
            int pret = pipe(pfd);

            //失败
            if(pret == -1)
                exit(PipeError);
            
            last_wfds.push_back(pfd[1]);
            //创建子进程,读任务，再执行
            pid_t id = fork();

            //失败
            if(id == -1)
            {
                exit(ForkError);
            }
            else if(id == 0)
            {
                //关闭子进程复制父进程前几个的写端，这里顺便就也关闭了当前自己不需要的写端
                //确保每个子进程都与父进程是单向通信
                for(auto& e : last_wfds) 
                {
                    close(e);
                }
                dup2(pfd[0], 0);
                work();
                exit(0);
            }

            //father
            close(pfd[0]);
            //创建进程池
            _vpool.push_back(Pipe_Child(pfd[1], id));

        }
    }

    //父进程向管道发送任务码
    void SecondTask(int task_node, int pp_node)
    { 
        write(_vpool[pp_node]._wfd, &task_node, sizeof(task_node));
    }

    //负载均衡，让每个子进程平均分配到任务
    int NextTaskPool()
    {
        static int n = 0;
        int next = n++;
        n %= _vpool.size();
        return next;
    }

    //关闭写端
    void CloseWrite()
    {
        for(auto& e : _vpool)
        {
            close(e._wfd);
        }
    }

    //回收子进程
    void WaitAll()
    {
        for(auto& e : _vpool)
        {
            waitpid(e._id, nullptr, 0);
        }
    }

    //任务结束
    void OverTask()
    {
        //保证不再写入，关闭写端
        CloseWrite();
        //回收子进程
        WaitAll();
    }
private:
    vector<Pipe_Child> _vpool;
    int _size;
};

void CtrlProcessPool(smartptr<process_pool>& ptr_pool, int times)
{
    while(times--)
    {
        //拿到派发任务的编号
        int task_node = GetNextTask();
        //拿到进程和管道的编号
        int pp_node = ptr_pool->NextTaskPool();
        //派发任务
        ptr_pool->SecondTask(task_node, pp_node);
        sleep(1);
    }
}
